//============================================================================
// Name        : builder.cpp
// Author      : Tony Meng
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <memory>

// Product
class Pizza
{
public:
	void setDough(const std::string& dough)
	{
		this->dough = dough;
	}
	void setSauce(const std::string& sauce)
	{
		this->sauce = sauce;
	}
	void setTopping(const std::string& topping)
	{
		this->topping = topping;
	}
	void showPizza() const
	{
		std::cout << "Dough: '" << dough\
				<< "' Sauce: '" << sauce \
				<< "' Toping: '" << topping \
				<< "'." << std::endl;
	}
private:
	std::string dough;
	std::string sauce;
	std::string topping;
};


// AbstractBuilder
class PizzaBuilder
{
public:
	virtual ~PizzaBuilder() {}
	std::auto_ptr<Pizza> getProduct()
		{
			return pizza;
		}
	void createNewPizzaProduct()
	{
		pizza.reset(new Pizza);
	}
	virtual void buildDough() = 0;
	virtual void buildSauce() = 0;
	virtual void buildTopping() = 0;
protected:
	Pizza& getPizza()
	{
		return *pizza;
	}
private:
	std::auto_ptr<Pizza> pizza;
};

// Concrete builders
class HawaiianPizzaBuilder : public PizzaBuilder
{
public:
	void buildDough()
	{
		getPizza().setDough("cross");
	}
	void buildSauce()
	{
		getPizza().setSauce("mild");
	}
	void buildTopping()
	{
		getPizza().setTopping("ham and pineapple");
	}
};

class SpicyPizzaBuilder : public PizzaBuilder
{
public:
	void buildDough()
	{
		getPizza().setDough("pan baked");
	}
	void buildSauce()
	{
		getPizza().setSauce("hot");
	}
	void buildTopping()
	{
		getPizza().setTopping("pepperoni and salami");
	}
};

// Director
class Waiter
{
public:
	Waiter() : pizzaBuilder(0) {}

	void setPizzaBuilder(PizzaBuilder& b)
	{
		pizzaBuilder = &b;
	}
	std::auto_ptr<Pizza> getPizza()
		{
			return pizzaBuilder->getProduct();
		}
	void constructPizza()
	{
		pizzaBuilder->createNewPizzaProduct();
		pizzaBuilder->buildDough();
		pizzaBuilder->buildSauce();
		pizzaBuilder->buildTopping();
	}
private:
	PizzaBuilder* pizzaBuilder;
};


int main() {
	Waiter waiter;

	HawaiianPizzaBuilder hawaiianPizzaBuilder;
	waiter.setPizzaBuilder(hawaiianPizzaBuilder);
	waiter.constructPizza();
	std::auto_ptr<Pizza> pizza = waiter.getPizza();
	pizza->showPizza();

	SpicyPizzaBuilder spicyPizzaBuilder;
	waiter.setPizzaBuilder(spicyPizzaBuilder);
	waiter.constructPizza();
	pizza = waiter.getPizza();
	pizza->showPizza();
	return 0;
}
